class Observable(object):
    """
    The Observer pattern with options to push or pull.

    The data may be pushed with each data change.
    Or the option of change notification, the data can then be pulled if needed.

    The default is push.
    push or pull are specified by adding a decorator to the callback

    XXX add option to ignore notifications where the data does not change.
    """
    
    def __init__(self, ival=None):
        """
        with an optional initial value
        """
        self._ival = ival
        self._interested = set() # use a set to store callbacks
        self.reset()

    def _report(self, func):
        """
        decide to push or wait for the data to be pulled.
        """
        if hasattr(func, 'push') and not func.push:
            func(self)
        else:
            func(self._value)
            
    def register(self, callback, iv=False):
        """
        when you register for notification, you can opt to be notified of the initial value
        """
        self._interested.add(callback)
        if iv:
            self._report(callback)
        return callback

    def deregister(self, callback):
        """
        when you no longer want to be notified
        """
        self._interested.discard(callback)

    def _set(self, value):
        """
        """
        self._value = value
        for callback in self._interested:
            self._report(callback)

    def _get(self):
        """
        """
        return self._value

    def reset(self):
        """
        set back to initial value
        """
        self.value = self._ival

    def __str__(self):
        """
        """
        return '%s (%s) = %s' % (self.__class__.__name__, self._ival, self._value)

    value = property(_get, _set) 

def push(f):
    """
    register a callback for push
    """
    f.push = True
    return f

def pull(f):
    """
    register a callback for pull
    """
    f.push = False
    return f
